<?php
/**
 * Created by PhpStorm
 * User: whh
 * Date: 2022/7/28
 * Time: 16:02
 */

namespace app\reception\logic;

use app\backstage\model\Faq;
use app\common\classes\BehaviorRedis;
use app\common\classes\helper\SendMessage;
use app\common\classes\VoucherRedis;
use app\common\model\Bookshelf;
use app\common\model\ChangeLog;
use app\common\model\Novel;
use app\common\model\NovelChapter;
use app\common\model\DefaultBookshelf;
use app\common\model\PhoneVerify;
use app\common\model\QuickApp;
use app\common\model\QuickLink;
use app\common\model\QuickUser;
use app\common\model\QuickUserUa;
use app\common\model\UserAddShortcut;
use app\common\model\UserBookPoints;
use app\common\model\UserConsume;
use app\reception\classs\AdvertBehavior;
use app\reception\server\ContentServer;
use think\Db;

class UserLogic
{

    /**
     * 快应用用户授权
     * @param int $quickAppId
     * @param array $params
     * @return array
     * @Author: whh
     */
    public function auth(int $quickAppId, array $params)
    {
        $where = [
            'userId' => $params['userId'],
            'quick_id' => $quickAppId,
            'phone' => '',
        ];
        $user = QuickUser::where($where)->find();
        $time = date("Y-m-d H:i:s");
        if ($user) {
            $user->login_time = $time;
            $user->save();
            /**
             * 分表同步
             */
            Db::table(QuickUser::fbTable($quickAppId))->where('id', $user->id)->update([
                'login_time' => $time,
            ]);
            return ['code' => 1, 'token' => $user->token, 'is_market' => $user->channel_id ? 0 : 1];
        } else {
            /**
             * 用户来源，理论上快应用是一定要带的，就算没有来源也会给空json数组{}
             */
            if (isset($params['storage'])) {
                $storage = json_decode($params['storage'], true);
                if ($storage === null) {
                    $storage = [];
                }
            } else {
                $storage = [];
            }
            /**
             * 链接判断，
             */
            $agent_id = 0;
            $channel_id = 0;
            $link_id = 0;
            $source_novel_id = 0;
            if (isset($storage['token']) && $storage['token']) {
                $linkInfo = QuickLink::where("token", $storage['token'])->where("is_delete", "<>", 2)->field("id,agent_id,channel_id,novel_id,quick_id,allow_register")->find();
                if ($linkInfo) {
                    if ($quickAppId != $linkInfo->quick_id) {
                        return ['code' => 0, 'msg' => '快应用信息对比失败，参数异常'];
                    }
                    if ($linkInfo->allow_register != 1){
                        return ['code' => 0, 'msg' => '链接状态异常'];
                    }
                    $agent_id = $linkInfo->agent_id;
                    $channel_id = $linkInfo->channel_id;
                    $link_id = $linkInfo->id;
                    $source_novel_id = $linkInfo->novel_id;
                }
            }
            $token = md5($quickAppId . '_' . $params['userId'] . '_' . rand(1, 1000000000))."_".rand(1, 9999999);//生成token，添加随机数防止和绑定号码的退出用户一致
            $saveData = [
                'quick_id' => $quickAppId,
                'agent_id' => $agent_id,
                'channel_id' => $channel_id,
                'link_id' => $link_id,
                'exp_time' => date("Y-m-d H:i:s"),//归属保护期过期时间，过期后允许切换归属
                'userId' => $params['userId'],
                'nickname' => '书友' . date('md') . rand(1000, 9999),
                'source_agent_id' => $agent_id,
                'source_channel_id' => $channel_id,
                'source_link_id' => $link_id,
                'source_novel_id' => $source_novel_id,
                'regdate' => date("Y-m-d"),
                'regtime' => $time,
                'login_time' => $time,
                'ip_addr' => request()->ip(),
                'token' => $token,
            ];
            $user = app('user');
            Db::startTrans();
            try {
                $saveData['id'] = $user->insertGetId($saveData);
                /**
                 * 分表同步
                 */
                Db::table(QuickUser::fbTable($quickAppId))->insert($saveData);
                /**
                 * ua表添加
                 */
                $ua = new QuickUserUa();
                $ua->ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
                $ua->uid = $saveData['id'];
                $ua->save();
                Db::commit();
                if ($link_id) {
                    $redis = new BehaviorRedis();
                    $redis->setLinkUv($link_id, $saveData['id']);
                }
                $result = ['code' => 1, 'token' => $token, 'is_market' => $saveData['channel_id'] ? 0 : 1];
            } catch (\Exception $e) {
                Db::rollback();
                $result = ['code' => 0, 'msg' => $e->getMessage()];
            }

            return $result;

        }
    }

    /**
     * 设置服务商
     * @param $user
     * @param $provider
     */
    public function setProvider($user, $provider)
    {
        $user->provider = $provider;
        $user->save();
        Db::table(QuickUser::fbTable($user->quick_id))->where('id', $user->id)->setField('provider', $provider);
    }

    /**
     * 订阅
     * @param $user
     * @param $reqId
     */
    public function subscribe($user, $reqId)
    {
        $user->regId = $reqId;
        $user->save();
        Db::table(QuickUser::fbTable($user->quick_id))->where('id', $user->id)->setField('regId', $reqId);
    }


    /**
     * 加桌
     * @param $uid
     * @Author: whh
     */
    public function addShortcut($user)
    {
        if ($user->add_shortcut == 0){
            $uid = $user->id;
            $ubp = new UserBookPoints();
            $where[] = ['uid', '=', $uid];
            $where[] = ['give_type', '=', $ubp::add_desktop];
            $flog = $ubp->where($where)->find();
            if (!$flog) {
                $point = TaskLogic::getTaskPoint('add_desktop', $user);
                $ubp->givePoint($uid, $point, $ubp::add_desktop);
            }
            /**
             * 记录加桌记录
             */
            $uaModel = new UserAddShortcut();
            $uaModel->uid = $uid;
            $uaModel->channel_id = $user->channel_id;
            $uaModel->agent_id = $user->agent_id;
            $uaModel->link_id = $user->link_id;
            $uaModel->ip_addr = request()->ip();
            $uaModel->regtime = $user->regtime;
            $uaModel->addshortcut_time = date("Y-m-d H:i:s");
            $uaModel->create_date = date("Y-m-d");
            $uaModel->save();
            /**
             * 更新加桌状态，因为有可能存在取消加桌再加桌的情况，所以add_shortcut_time也更新
             */
            $user->add_shortcut = 1;
            $upData['add_shortcut'] = 1;
            $time = date("Y-m-d H:i:s");
            $user->add_shortcut_time = date("Y-m-d H:i:s");
            $upData['add_shortcut_time'] = $time;
            $user->save();
            Db::table(QuickUser::fbTable($user->quick_id))->where('id', $user->id)->update($upData);
        }
    }


    /**
     * 发送验证码
     * @param $quickAppId
     * @param $uid
     * @param $phone
     * @return array|\think\response\Json
     * @Author: whh
     */
    public function sendSms($quickAppId, $uid, $phone)
    {

        $phoneVerify = new PhoneVerify();
        $num = $phoneVerify->where('phone', $phone)->whereTime('send_time', 'd')->count();
        if ($num >= 10) {
            return errArr('一天最多获取10条验证码');
        }
        $code = rand(1000, 9999);
        $send_time = time();
        $where = [
            ['phone', '=', $phone],
            ['send_time', '>', $send_time - 59],
        ];
        $flag = $phoneVerify->where($where)->find();
        if ($flag) {
            return errArr('请勿重复发送');
        }
        $info = QuickApp::get($quickAppId)->info;
        $signName = $info->sms_sign;
        $argv = [
            "phoneNumbers" => $phone,
            "signName" => $signName,
            "templateCode" => config('sms_templateCode'),
            "templateParam" => "{\"code\":\"{$code}\"}"
        ];
        $res = SendMessage::main($argv);
        if ($res['code'] == 1) {
            $phoneVerify->save([
                'phone' => $phone,
                'code' => $code,
                'send_time' => time(),
                'uid' => $uid
            ]);
        }
        return $res;
    }


    /**
     * 绑定手机或切换登录
     * @param QuickUser $own
     * @param $phone
     * @param $code
     * @return \think\response\Json
     * @throws \think\Exception
     * @throws \think\exception\PDOException
     */
    public function binding(QuickUser $own, $phone, $code)
    {
        /**
         * 一个快应用，对应一个手机号
         */
        $phoneVerify = new PhoneVerify();
        $codeWhere = [
            ['phone', '=', $phone],
            ['code', '=', $code],
            ['send_time', '>', time() - (60 * 5)]
        ];
        $verifyFlag = $phoneVerify->where($codeWhere)->find();
        if (!$verifyFlag) {
            return errJson('验证码无效');
        }
        $user = app('user');
        $userWhere[] = ['phone', '=', $phone];
        $userWhere[] = ['quick_id', '=', $own->quick_id];
        $bindFlag = $user->where($userWhere)->find();
        if (!$bindFlag) {
            $own->phone = $phone;
            $own->nickname = $phone;
            $own->save();
            Db::table(QuickUser::fbTable($own->quick_id))->where('id', $own->id)->update([
                'phone' => $phone,
                'nickname' => $phone,
            ]);
            $ubp = new UserBookPoints();
            /**
             * 自定义赠送书点
             */
            $point = TaskLogic::getTaskPoint('bind_number', $own);
            $ubp->givePoint($own->id, $point, $ubp::binding);
            $data['user_token'] = $own->token;
        } else {
            $data['user_token'] = $bindFlag->token;
        }
        return sucJson($data);
    }


    /**
     * 用户变更
     * @param $user
     * @param $link
     * @param $isWhite
     * @Author: whh
     */
    public static function changeUser($user, $link, $isWhite)
    {
        /**
         * 如果用户没有来源信息，就不能变更归属，除非读者是白名单
         */
        if ($user->userId != '36cecec57756f3d27' && (($user->source_agent_id && $user->source_channel_id && $user->source_link_id) || $isWhite)) {

            if ($user->link_id != $link->id || $user->channel_id != $link->channel_id || $user->agent_id != $link->agent_id) {

                if ($user->channel_id != $link->channel_id || $user->agent_id != $link->agent_id){
                    /**
                     * 如果要切换代理，或者投放号，则需要大于保护期，否则则中断变更
                     */
                    if ($user->exp_time > date("Y-m-d H:i:s")){
                        return false;
                    }
                }
                Db::startTrans();
                try {
                    /**
                     * 变更用户归属
                     */
                    $user->channel_id = $link->channel_id;
                    $user->agent_id = $link->agent_id;
                    $user->link_id = $link->id;
                    $user->save();
                    Db::table(QuickUser::fbTable($user->quick_id))->where('id', $user->id)->update([
                        'channel_id' => $link->channel_id,
                        'agent_id' => $link->agent_id,
                        'link_id' => $link->id,
                    ]);
                    /**
                     * 变更日志记录
                     */
                    $changeModel = new ChangeLog();
                    $changeModel->uid = $user->id;
                    $changeModel->channel_id = $link->channel_id;
                    $changeModel->agent_id = $link->agent_id;
                    $changeModel->link_id = $link->id;
                    $changeModel->change_date = date("Y-m-d");
                    $changeModel->change_time = time();
                    $changeModel->save();
                    Db::commit();
                } catch (\Exception $e) {
                    Db::rollback();
                    /**
                     * 记录日志，别影响后续阅读
                     */
                    $content = json_encode(['line' => $e->getLine(), 'msg' => $e->getMessage()]);
                    LogLogic::addLog('changeUser', $content, $user->id, $user->quick_id);
                }
            }
        }
    }


    /**
     * 用户阅读内容
     * @param $user
     * @param $novel_id
     * @param $chapter_no
     * @return \think\response\Json
     * @throws \think\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public static function read($user, $novel_id, $chapter_no)
    {
        $novel = Novel::where('id', $novel_id)->field("id,origin_title,words_fee,cover")->find();
        if (!$novel) {
            return errJson('书籍不存在');
        }
        $chapterWhere[] = ['novel_id', '=', $novel_id];
        $chapterWhere[] = ['chapter_no', '=', $chapter_no];
        $chapter = NovelChapter::where($chapterWhere)->field("name,novel_id,chapter_no,is_pay,words")->find();
        if (!$chapter) {
            return errJson('章节不存在');
        }
        $contentRes = $chapter->getContent();
        if (!$contentRes) {
            return errJson('内容不存在');
        }
        $behaviorRedis = new BehaviorRedis();
        $behaviorRedis->addReading($user->id, $novel_id);
        $chapter = $chapter->toArray();
        $chapter['novel_title'] = $novel->origin_title;
        $chapter['cover'] = config('img_domain') . $novel->cover;
        $count = $contentRes['content'];

        $chapter['content'] = $count;
        if ($chapter_no <= 1) {
            $chapter['previous_chapter_no'] = null;
        } else {
            $chapter['previous_chapter_no'] = $chapter_no - 1;
        }
        $chapterWhere = [];
        $chapterWhere[] = ['novel_id', '=', $novel_id];
        $maxNo = NovelChapter::where($chapterWhere)->cache(3600)->max('chapter_no');
        $chapter['next_chapter'] = $chapter_no + 1 > $maxNo ? null : $chapter_no + 1;
        return self::setReadRecord($user, $chapter,$novel);
    }

    /**
     * 记录阅读记录
     * @param $user
     * @param $chapter
     * @param $novel
     * @return mixed
     */
    protected static function setReadRecord($user, $chapter, $novel)
    {
        $words_fee = NovelLogic::getNovelWordsFee($user, $novel);
        $voucherRedis = new VoucherRedis();
        if ($user->vip_exp_time >= time()) {//会有判断
            $chapter['is_pay'] = 0;
        }
        if ($chapter['is_pay'] == 1) {
            //消费凭证判断
            $checkResult = checkReadVoucher($user->id,$chapter['novel_id'],$chapter['chapter_no']);
            if ($checkResult) {
                $chapter['is_pay'] = 0;
            }
        }
        $chapter['allowRead'] = true;//允许阅读？不允许uv和阅读记录不记录（当书币不足时就不用记了，不然书币不足只返回一部分内容，记阅读记录和uv岂不是扯淡）
        $chapter['consume_pop'] = false;//消费弹窗
        $used_all = 0;//总消耗
        $used_point = 0;//使用书点
        $used_coin = 0;//使用书币
        $chapter['price'] = 0;
        if ($chapter['is_pay'] == 1) {
            if (!$user->channel_id){
                $chapter['consume_pop'] = true;
            }
            $multiple = sprintf("%.1f", $chapter['words'] / 1000);
            $chapter['price'] = $used_all = intval($multiple * $words_fee);
            $result = self::consumption($user, $used_all);
            if ($result['code'] != 1) {
                $chapter['allowRead'] = false;
                $chapter['content'] = mb_substr($chapter['content'], 0, 6) . "……";
            } else {
                $used_point = $result['data']['used_point'];
                $used_coin = $result['data']['used_coin'];
                $voucherRedis->insertReadVoucher($user->id, $chapter['novel_id'], $chapter['chapter_no']);//插入消费凭证
            }
        }
        /**阅读明细*/
        if ($chapter['allowRead']) {
            $recordTable = "quick_user_read_record_" . date("Ym");
            $recordSave = [
                'uid' => $user->id,
                'quick_id' => $user->quick_id,
                'novel_id' => $chapter['novel_id'],
                'chapter_no' => $chapter['chapter_no'],
                'pay_read' => $chapter['is_pay'],
                'used_all' => $used_all,
                'used_point' => $used_point,
                'used_coin' => $used_coin,
                'channel_id' => $user->channel_id,
                'agent_id' => $user->agent_id,
                'read_date' => date("Y-m-d")
            ];
            Db::table($recordTable)->insert($recordSave);

            if ($chapter['is_pay'] == 1) {
                Db::table('quick_user_consume_record')->insert(['uid'=>$user->id,'novel_id'=>$chapter['novel_id'],'chapter_no' => $chapter['chapter_no']]);
            }
        }
        /**用户阅读记录*/
        self::refreshBookshelf($user->id, $chapter['novel_id'], $chapter['chapter_no']);
        return $chapter;
    }


    /**
     * 刷新书架
     * @param $uid
     * @param $novelId
     * @param $chapterNo
     */
    public static function refreshBookshelf($uid, $novelId, $chapterNo)
    {
        $where[] = ['uid', '=', $uid];
        $where[] = ['novel_id', '=', $novelId];
        $id = Bookshelf::withTrashed()->where($where)->value('id');
        if ($id) {
            Db::table("quick_user_bookshelf")->where("id", $id)->update([
                'chapter_no' => $chapterNo,
                'read_time' => date("Y-m-d H:i:s"),
                'delete_time' => 0,
            ]);
        } else {
            Bookshelf::insert([
                'uid' => $uid,
                'novel_id' => $novelId,
                'chapter_no' => $chapterNo,
            ]);
        }
    }


    /**
     * 扣费
     * @param $user
     * @param $used
     * @return array
     */
    public static function consumption($user, $used)
    {
        $userPointModel = model('UserBookPoints');
        $point = $userPointModel->getBalancePoint($user->id);
        Db::startTrans();
        try {
            if ($point >= $used) {
                //书点全额支付
                $userPointModel->usePoint($user->id, $used);
                $used_point = $used;//使用书点
                $used_coin = 0;//使用书币
            } else {
                //查询用户书币余额
                $balance = $user->book_coin + $point;
                if ($balance >= $used) {
                    /**
                     * 书币加书点够支付
                     */
                    $used_point = 0;
                    $used_coin = $used;
                    if ($point) {
                        $used_coin = $userPointModel->usePoint($user->id, $used);
                        $used_point = $used - $used_coin;
                    }
                    $book_coin = $user->book_coin - $used_coin;
                    $user->book_coin = $book_coin;
                    $user->save();
                    Db::table(QuickUser::fbTable($user->quick_id))->where('id', $user->id)->update([
                        'book_coin' => $book_coin
                    ]);
                } else {
                    return errArr('用户余额不足');
                }
            }
            /**
             * 添加消费记录
             */
            $userConsume = UserConsume::where("uid", $user->id)->find();
            if ($userConsume) {
                $userConsume->used_all = $userConsume->used_all + $used;
                $userConsume->used_point = $userConsume->used_point + $used_point;
                $userConsume->used_coin = $userConsume->used_coin + $used_coin;
            } else {
                $userConsume = new UserConsume();
                $userConsume->uid = $user->id;
                $userConsume->used_all = $used;
                $userConsume->used_point = $used_point;
                $userConsume->used_coin = $used_coin;
            }
            $userConsume->save();
            Db::commit();
            return sucArr(['used_point' => $used_point, 'used_coin' => $used_coin]);
        } catch (\Exception $e) {
            Db::rollback();
            $content = json_encode(['line' => $e->getLine(), 'msg' => $e->getMessage()]);
            LogLogic::addLog('consumption', $content, $user->id, $user->quick_id);
            return errArr("扣费失败：line:{$e->getLine()},msg:{$e->getMessage()}");
        }
    }


    /**
     * 获取用户阅读记录
     * @param $user
     * @param $isWhite
     * @param $isBlack
     * @return \think\response\Json
     */
    public function getBookshelf($user, $isWhite, $isBlack)
    {
        $where[] = ['uid', '=', $user->id];
        $filed = "novel_id,read_time,chapter_no";
        $blackNovelIds = Novel::where("in_shelf", "=", 0)->cache(1200)->column('id');//绝对下架不展示
        if ($blackNovelIds) {
            $where[] = ['novel_id', 'not in', $blackNovelIds];
        }
        $list = Bookshelf::field($filed)->where($where)->with(['novel'])->order("read_time desc")->select();
        $list = $list->toArray();
        /**
         * 白名单 || (有归属渠道，并且不是黑名单，并且不是疑似审核员 查询热榜推荐位)
         */
        $hotNovels = [];
        $listIdArr = array_column($list, 'novel_id');
        if ($isWhite || ($user->channel_id && $isBlack == 0 && !$user->auditor)) {
            /**查询推荐书籍*/
            $defaultWhere[] = ['quick_id', '=', $user->quick_id];
            $defaultWhere[] = ['novel_id', 'not in', $listIdArr];//书架有的就推荐了
            if ($isWhite || ipUnblocked($user)) {
                $defaultWhere[] = ['type', '=', 1];
            } else {
                $defaultWhere[] = ['type', '=', 2];
            }
            $defaultNIds = DefaultBookshelf::where($defaultWhere)->column('novel_id');
            /**
             * 条件：上架、专属判断
             */
            $opNWhere[] = ['id', 'in', $defaultNIds];
            $opNWhere[] = ['in_shelf', '=', 1];
            $filed = "id,cover,origin_title novel_title,author,freq_id,class_id,update_status,chapter_up_time";
            $hotNovels = Novel::field($filed)->where($opNWhere)->orderRand()->limit(9)->select();
            $hotNovels = $hotNovels->toArray();
        }
        $cidArr = [];
        $fidArr = [];
        foreach ($list as $k => $v) {
            $cidArr[] = $v['novel']['class_id'];
            $fidArr[] = $v['novel']['freq_id'];
        }
        $cidArr = array_unique(array_merge($cidArr, array_column($hotNovels, 'class_id')));
        $fidArr = array_unique(array_merge($fidArr, array_column($hotNovels, 'freq_id')));
        $nidArr = array_unique(array_merge($listIdArr, array_column($hotNovels, 'id')));
        $classList = ContentServer::plGetNovelClass($cidArr);
        $freqList = ContentServer::plGetNovelFreq($fidArr);
        $maxNoList = ContentServer::plGetMaxChapterNo($nidArr);

        foreach ($list as $k => $v) {
            $list[$k]['novel']['cover'] = config('img_domain') . $v['novel']['cover'];
            //查询总章节数
            $list[$k]['novel']['novel_max_chapter_no'] = isset($maxNoList[$v['novel_id']]) ? $maxNoList[$v['novel_id']] : 0;
            $list[$k]['novel']['freq'] = isset($freqList[$v['novel']['freq_id']]) ? $freqList[$v['novel']['freq_id']] : '';
            $list[$k]['novel']['novel_class'] = isset($classList[$v['novel']['class_id']]) ? $classList[$v['novel']['class_id']] : '';
            $list[$k]['novel_title'] = $v['novel']['origin_title'];

            /**重新组装结构*/
            $list[$k] = array_merge($list[$k], $list[$k]['novel']);
            unset($list[$k]['novel']);
            unset($list[$k]['id']);
            unset($list[$k]['origin_title']);
        }
        foreach ($hotNovels as $k => $v) {
            $hotNovels[$k]['cover'] = config('img_domain') . $v['cover'];
            //查询总章节数
            $hotNovels[$k]['novel_max_chapter_no'] = isset($maxNoList[$v['id']]) ? $maxNoList[$v['id']] : 0;
            $hotNovels[$k]['freq'] = isset($freqList[$v['freq_id']]) ? $freqList[$v['freq_id']] : '';
            $hotNovels[$k]['novel_class'] = isset($classList[$v['class_id']]) ? $classList[$v['class_id']] : '';
            $hotNovels[$k]['novel_id'] = $v['id'];
            unset($hotNovels[$k]['id']);
        }
        // 检查第一个数组是否只有一个值
        if (count($list) == 1) {
            // 如果是，将第二个数组的值插入到索引为1的位置
            array_splice($list, 1, 0, $hotNovels);
        } else {
            // 如果不是，将第二个数组的值插入到索引为3的位置
            array_splice($list, 3, 0, $hotNovels);
        }
        return sucJson($list);
    }


    /**
     * @param $user
     * @return \think\response\Json
     */
    public function getFeedbackList($user){
        $where[] = ['uid', '=', $user->id];
        $where[] = ['type', '=', 1];
        $model = new Faq();
        $field = "id,uid,content,create_time";
        $list = $model->where($where)->field($field)->order("id desc")->select();
        foreach ($list as $k => $v){
            $list[$k]['comments'] = $model->comments($v['id']);
        }
        return sucJson($list);
    }

}